Solutions/Tenable App/Data Connectors/TenableVM/exports_store.py (138 lines of code) (raw):

import logging from enum import Enum from azure.data.tables import TableClient, UpdateMode from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError, HttpResponseError from .tenable_helper import TenableStatus class ExportsTableStore: def __init__(self, connection_string, table_name): self.connection_string = connection_string self.table_name = table_name def create(self): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: try: table_client.create_table() except ResourceExistsError: logging.warning("Table already exists") def post(self, pk: str, rk: str, data: dict = None): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: entity_template = { "PartitionKey": pk, "RowKey": rk, } if data is not None: entity_template.update(data) try: table_client.create_entity(entity_template) except Exception as e: logging.warning("could not post entity to table") logging.warning(e) raise e def get(self, pk: str, rk: str): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: try: logging.info( f"looking for {pk} - {rk} on table {self.table_name}") return table_client.get_entity(pk, rk) except ResourceNotFoundError: return None def upsert(self, pk: str, rk: str, data: dict = None): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: logging.info(f"upserting {pk} - {rk} on table {self.table_name}") entity_template = { "PartitionKey": pk, "RowKey": rk, } if data is not None: entity_template.update(data) return table_client.upsert_entity(mode=UpdateMode.REPLACE, entity=entity_template) def update_if_found(self, pk: str, rk: str, data: dict = None): if self.get(pk, rk) is not None: self.merge(pk, rk, data) def query_by_partition_key(self, pk): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {u"key": pk} name_filter = u"PartitionKey eq @key" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_finished_chunks_by_partition_key(self, pk): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {"key": pk, "status": TenableStatus.finished.value} name_filter = "PartitionKey eq @key and jobStatus eq @status" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_all_finished_chunks(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {"status": TenableStatus.finished.value} name_filter = "jobStatus eq @status" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_failed_chunks_by_partition_key(self, pk): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {"key": pk, "status": TenableStatus.failed.value} name_filter = "PartitionKey eq @key and jobStatus eq @status" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_all_failed_chunks(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = {"status": TenableStatus.failed.value} name_filter = "jobStatus eq @status" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def query_for_all_processing_chunks(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) parameters = { "failedStatus": TenableStatus.failed.value, "processingStatus": TenableStatus.processing.value, "sentStatus": TenableStatus.sent_to_queue.value, "sendingStatus": TenableStatus.sending_to_queue.value } name_filter = "jobStatus eq @failedStatus or jobStatus eq @processingStatus or jobStatus eq @sentStatus or jobStatus eq @sendingStatus" try: return table_client.query_entities(name_filter, parameters=parameters) except HttpResponseError as e: print(e.message) return [] def batch(self, operations): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: return table_client.submit_transaction(operations=operations) def list_all(self): table_client = TableClient.from_connection_string( self.connection_string, self.table_name) return table_client.list_entities() def merge(self, pk: str, rk: str, data: dict = None): with TableClient.from_connection_string(self.connection_string, self.table_name) as table_client: logging.info(f"upserting {pk} - {rk} on table {self.table_name}") entity_template = { "PartitionKey": pk, "RowKey": rk, } if data is not None: entity_template.update(data) return table_client.upsert_entity(mode=UpdateMode.MERGE, entity=entity_template) class ExportsTableNames(Enum): TenableExportStatsTable = "TenableExportStatsTable" TenableAssetExportTable = "TenableAssetExportTable" TenableVulnExportTable = "TenableVulnExportTable" TenableComplianceExportTable = "TenableComplianceExportTable" TenableExportCheckpointTable = "TenableExportCheckpointTable"